#include <hal/apic.h>
#include <arch/io.h>
#include <arch/interrupts.h>
#include <arch/vectors.h>
#include <mm/mmio.h>

#define LOG_TAG "APIC"
#include <log.h>

static volatile uptr apic_base;

u32 apic_read(u32 reg) {
    return *(u32*)(apic_base + reg);
}

void apic_write(u32 reg, u32 val) {
    *(u32*)(apic_base + reg) = val;
}

void apic_setup_lvt() {
    apic_write(APIC_LVT_LINT0, APIC_LINT0_IV);
    apic_write(APIC_LVT_LINT1, LVT_DELIVERY_NMI | LVT_MASKED | LVT_TRIGGER_EDGE);
    apic_write(APIC_LVT_ERROR, APIC_ERROR_IV);
}
void timer_update();
void apic_init() {
    // disable interrupts from PIC
    outb(0x21, 0xFF);
    outb(0xA1, 0xFF);

    apic_base = ioremap(APIC_BASE_PA, 1);

    asm volatile ("movl %0, %%ecx\n"
        "rdmsr\n"
        "orl %1, %%eax\n"
        "wrmsr\n"
        ::"i"(IA32_MSR_APIC_BASE), "i"(IA32_APIC_ENABLE)
        : "eax", "ecx", "edx"
        );

    u32 apic_id = apic_read(APIC_IDR) >> 24;
    u32 apic_ver = apic_read(APIC_VER);

    LOGI("ID: %x, Version: %x, Max LVT: %u\n",
        apic_id,
        apic_ver & 0xff,
        (apic_ver >> 16) & 0xff
    );

    apic_setup_lvt();
    // initialize priority registers
    // set the task priority to the lowest possible, so all external interrupts
    // are acceptable
    //   Note, the lowest possible priority class is 2, not 0, 1, as they are
    //   reserved for internal interrupts (vector 0-31, and each p-class
    //   resposible for 16 vectors). See Intel Manual Vol. 3A, 10-29
    apic_write(APIC_TPR, APIC_PRIORITY(2, 0));

    // Enable APIC and install our handler for spurious interrupt.
    u32 spiv = apic_read(APIC_SPIVR);
    spiv = (spiv & ~0xFF) | APIC_SPIV_APIC_ENABLE | APIC_SPIV_IV;
    apic_write(APIC_SPIVR, spiv);
}

void apic_done_service() {
    *(u32*)(apic_base + APIC_EOI) = 0;
}